/**
 * @description: 通用的AJAX请求封装，
 * @return 使用方式 import Request, { $request, $get, $post, $put, $delete } from '@/utils/request';
 */
import Taro from '@tarojs/taro';
import axios from 'axios';

import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError, Canceler, InternalAxiosRequestConfig } from 'axios';

const sourceAjaxList: Canceler[] = [];

const clearStorage = () => {
  Taro.clearStorage();
};

const showToast = (message: string) => {
  Taro.showToast({
    title: message,
    icon: 'none',
    duration: 2000,
  });
};

/**
 * @description: 需要主动调用new Request(), 可传递更多配置参数进去
 * @return {*}
 */
export class Request {
  instance: AxiosInstance;

  baseConfig: AxiosRequestConfig = {
    headers: {
      'X-Gaia-Api-Key': process.env.TARO_APP_GAIA_API_KEY,
    },
    baseURL: process.env.TARO_APP_BASE_API,
    timeout: 30000,
    withCredentials: true,
  };

  constructor(axiosConfig?: AxiosRequestConfig) {
    this.instance = axios.create(Object.assign(this.baseConfig, axiosConfig));

    this.instance.interceptors.request.use(
      (requestConfig: InternalAxiosRequestConfig) => {
        // 设置 cancel token  用于取消请求 (当一个接口出现401后，取消后续多有发起的请求，避免出现好几个错误提示)
        requestConfig.cancelToken = new axios.CancelToken((cancel: Canceler): void => {
          sourceAjaxList.push(cancel);
        });

        // 这里一个token，你需要在这里取到你设置好的token的值 示例逻辑代码
        const token = Taro.getStorageSync('_user_info')?.authorization || '';
        const tenantId = Taro.getStorageSync('_user_info')?.currentTenantId || '';
        if (token) {
          // 这里将token设置到headers中，header的key是Authorization，这个key值根据你的需要进行修改即可
          requestConfig.headers = {
            Authorization: token,
            tenantId,
            ...requestConfig.headers,
          } as any;
        }

        requestConfig.paramsSerializer = {
          indexes: null,
        };

        return requestConfig;
      },
      (error: any) => Promise.reject(error)
    );

    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const requestUrl = response.config.url || '';
        const { data: result } = response;

        // 返回数据的code 不为200即为数据出错
        if (+result.code === 0 || +result.code === 200) {
          return result;
        }
        if (+result.code === 401) {
          clearStorage();
          showToast('权限校验失效，需要重新登录');
          throw new Error(`${result.loginUrl} 需要重新登录`);
        }
        if (+result.code === 403) {
          showToast(`${requestUrl} 暂无访问权限`);
          throw new Error(`${requestUrl} 暂无访问权限`);
        }

        showToast(result.message || result.msg || 'Error');
        throw new Error(result.message || result.msg || 'Error'); // 预期结果失败
      },
      (err: AxiosError) => {
        const requestUrl = err?.config?.url || '';
        if (err.toString().includes('timeout')) {
          showToast('请求超时，请稍后再试');
        }
        if (err && err.response) {
          const { status, config }: any = err.response;
          switch (status) {
            case 400:
              err.message = '请求错误';
              break;

            case 401:
              // 用户登录信息认证失败，退出到登录页之前清空缓存内容
              clearStorage();
              // 出现未登录或登录失效取消后面的请求
              if (sourceAjaxList.length > 0) {
                sourceAjaxList.forEach((ajaxCancel, index) => {
                  ajaxCancel(); // 取消请求
                  delete sourceAjaxList[index];
                });
              }
              break;

            case 403:
              err.message = `${requestUrl} 暂无访问权限`;
              break;

            case 404:
              err.message = `请求地址出错: ${config.url}`;
              break;

            case 408:
              err.message = '请求超时';
              break;

            case 500:
            case 501:
            case 502:
            case 503:
            case 504:
              err.message = '服务器开个小差，请稍后再试';
              break;

            case 505:
              err.message = 'HTTP版本不受支持';
              break;

            default:
              err.message = '请求失败，请稍后再试';
              break;
          }
          showToast(err.message || 'Error');
        }
        return Promise.reject(err);
      }
    );
  }

  public request<T = any>(config: AxiosRequestConfig): Promise<T> {
    return this.instance.request(config);
  }

  public get<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.get(url, config);
  }

  public post<T = any>(
    url: string,
    data?: App.TypeObject,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.post(url, data, config);
  }

  public put<T = any>(
    url: string,
    data?: App.TypeObject,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.put(url, data, config);
  }

  public delete<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.delete(url, config);
  }
}

/**
 * @description: 封装默认配置的axios的request、get、post、put、delete
 * @return {*}
 */
const server = new Request();
const $request = <T = any>(config: AxiosRequestConfig): Promise<T> => server.request(config);

const $get = <T = any>(
  url: string,
  config?: AxiosRequestConfig
): Promise<T> => server.get(url, config);

const $post = <T = any>(
  url: string,
  data?: App.TypeObject,
  config?: AxiosRequestConfig
): Promise<T> => server.post<T>(url, data, config);

const $put = <T = any>(
  url: string,
  data?: App.TypeObject,
  config?: AxiosRequestConfig
): Promise<T> => server.put<T>(url, data, config);

const $delete = <T = any>(
  url: string,
  config?: AxiosRequestConfig
): Promise<T> => server.delete<T>(url, config);

export {
  $request,
  $get,
  $post,
  $put,
  $delete,
};
export default server;
